﻿using System;
using System.Collections.Generic;
using System.Reflection;

namespace WP7Tools.DI {
    public class ServiceLocator : IServiceLocator {
        private Dictionary<Type, Type> _typeRegistrations;
        private Dictionary<Type, object> _instanceRegistrations;

        private static IServiceLocator _instance;

        public static IServiceLocator Default {
            get {
                if(_instance == null) {
                    _instance = new ServiceLocator();
                }
                return _instance;
            }
        }

        private ServiceLocator() {
            _typeRegistrations = new Dictionary<Type, Type>();
            _instanceRegistrations = new Dictionary<Type, object>();
        }

        public void Register<TIntf, TClass>()
            where TIntf : class
            where TClass : TIntf {
            if (DoesRegistrationExist<TIntf>())
                throw new DuplicateRegistrationException("Can only contain one registration per type");
            _typeRegistrations.Add(typeof (TIntf), typeof (TClass));
        }

    	public void RegisterInstance<TIntf>(TIntf instance) {
    		if (DoesRegistrationExist<TIntf>()) {
    			throw new DuplicateRegistrationException("Can only contain one registration per type");
    		}
    		_instanceRegistrations.Add(typeof (TIntf), instance);
    	}

    	public TIntf Resolve<TIntf>() where TIntf : class {
            return Resolve(typeof (TIntf)) as TIntf;
        }

    	private object Resolve(Type type) {
            if (!_typeRegistrations.ContainsKey(type)) {
                if (_instanceRegistrations.ContainsKey(type)) {
                    return _instanceRegistrations[type];
                }
                if(type.IsAbstract || type.IsInterface) {
                    throw new NotSupportedException("Cannot find registration for type " + type.FullName + ".");                               
                }
                _typeRegistrations.Add(type, type);
            }
            var createdType = _typeRegistrations[type];

			ConstructorInfo mostSpecificConstructor = GetMostSpecificConstructor(createdType);

        	List<object> constructorParameters = new List<object>();
            foreach (var a in mostSpecificConstructor.GetParameters()) {
                constructorParameters.Add(Resolve(a.ParameterType));
            }
            return Activator.CreateInstance(createdType, constructorParameters.ToArray());
        }

    	private static ConstructorInfo GetMostSpecificConstructor(Type type) {
			ConstructorInfo[] constructors = type.GetConstructors();
    		ConstructorInfo mostSpecificConstructor = null;
    		foreach (var c in constructors) {
    			if (mostSpecificConstructor == null ||
    			    mostSpecificConstructor.GetParameters().Length < c.GetParameters().Length) {
    				mostSpecificConstructor = c;
    			}
    		}
    		return mostSpecificConstructor;
    	}

    	private bool DoesRegistrationExist<T>() {
            return _instanceRegistrations.ContainsKey(typeof (T)) || _typeRegistrations.ContainsKey(typeof (T));
        }
    }
}